/*
 * Copyright 2014 Will Dignazio
 */
#include <socketio.h>
#include <sys/queue.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

#define PROTO_HTTP	"http"
#define PROTO_HTTPS	"https"

enum
{
	TRANS_XHR_POLLING	= 1 << 0,
	TRANS_XHR_MULTIPART	= 1 << 1,
	TRANS_HTMLFILE		= 1 << 2,
	TRANS_WEBSOCKET		= 1 << 3,
	TRANS_FLASHPOCKET	= 1 << 4,
	TRANS_JSONP_POLLING	= 1 << 5,
};

typedef struct WorkItem		WorkItem;
typedef struct WorkQueue	WorkQueue;

/* Global Work Queue */
extern WorkQueue	*global_workqueue;

typedef struct SocketRequest	SocketRequest;
typedef struct SocketData	SocketData;

typedef int (*SocketCallback)(SocketData*, SocketSession*);

struct SocketData
{
	union
	{
		char *cdata;
		void *vdata;
	};

	size_t	bsize;	// Size of data elements
	size_t	nelem;	// Number of elements
};

struct SocketSession
{
	char		*sid;			// Session ID
	uint32_t	heartbeat_timeout;	// Heartbeat Interval (timeout)
	uint32_t	close_timeout;		// Close timeout
	uint32_t	transtypes;		// Transport types supported
};

struct SocketRequest
{
        char	*scheme;
	char	*host;
	char	*ns;
	int	proto;
	int	tid;
	int	sid;
	char	*query;
};

/* Request/Management API */
extern int		postrequest(SocketRequest *request, SocketSession *session, SocketCallback callback);
extern SocketData*	allocdata(char *cdata, size_t bsize, size_t nelem);
extern void		freedata(SocketData *sdata);
extern SocketRequest*	allocrequest(char *scheme, char *host, char *ns, int proto, int tid, int sid, char *query);
extern void		freerequest(SocketRequest *req);

/* Handshaking */
extern int		acknowledge(SocketData *sdata, SocketSession *session);
extern int		handshake(SocketData *sdata, SocketSession *session);

/* Sessions */
extern SocketSession*	allocsession(void);
extern void		freesession(SocketSession *session);
extern int		startsession(char *sid, uint32_t heart_to, uint32_t cose_to, uint32_t transtypes, SocketSession *session);

/* Work Queues */
extern int		work_enqueue(SocketCallback work, SocketData *sdata, SocketSession *session);
extern WorkQueue	*allocworkqueue(void);
extern void		freeworkqueue(WorkQueue *wqueue);

/*
 * Queue API
 */
enum
{
	QUEUE_CIRCULAR	= 1 << 0,	// Circular Buffer
	QUEUE_RO	= 1 << 1,	// Read Only Mode
	QUEUE_HEADED	= 1 << 2,	// Must have head element
	QUEUE_NOSIGNAL	= 1 << 3,	// Queue does not signal on conditional
	QUEUE_NOLOCK	= 1 << 4,	// Don't wait for the lock
	QUEUE_BROADCAST	= 1 << 5,	// Broadcast rather than signal
} QUEUE_FLAGS;

enum
{
	QUEUE_SUCCESS,
	QUEUE_FAIL,
	QUEUE_EMPTY,
	QUEUE_ISRO,
	QUEUE_INTERNAL,
};

typedef struct Queue Queue;
typedef struct QueueElement QueueElement;

struct Queue
{
	QueueElement*	head;	// First element in queue
	QueueElement*	tail;	// Last element in queue
	uint64_t	size;	// Size of queue
	uint8_t		flags;	// Queue flags
	pthread_mutex_t	lock;	// Queue lock
	pthread_mutex_t siglock;// Internal Signal lock
	pthread_cond_t	signal;	// Queue signal
};

extern Queue*	queue_create(uint8_t flags);
extern int	queue_push(Queue *queue, WorkItem *elemptr);
extern int	queue_pop(Queue *queue, WorkItem **ref);
extern int	queue_peek(Queue *queue, WorkItem **ref);
extern pthread_cond_t* queue_getsignal(Queue *queue);
extern int	queue_wait(Queue *queue);

extern int	queue_empty(Queue *queue);
extern uint64_t	queue_size(Queue *queue);
extern int	queue_free(Queue *queue);
